/**********************************************************
                    DEF: lock.c
Written by IL
Greatly edited by CAS
*********************************************************/
#include "fpp.h"
#include <pwd.h>
#include <sys/stat.h>
#include "dirent.h"
#include <stdio.h>
#include <unistd.h>

/*efriedr*/
#define graphQuery messQuery

extern int Zbatch_flag;
void fpc_unlockit();
static int lockFd = -1;
int save_fpc();

/************************************************************
               DEF: hasdatechanged
************************************************************/
static BOOL hasdatechanged()
{
  FILE *tempfile; 
  char line[255];
  char fpcfile[MAXPATHLEN];
   
  if (strlen(dirName) > 0) sprintf(fpcfile,"%s/%s.fpc", dirName, fileName);
  else sprintf(fpcfile,"%s.fpc", fileName);

  if((tempfile = fopen(fpcfile,"r"))==NULL ){
      printf("File %s cannot be open for reading.\n", fpcfile);
      return FALSE;
  }
  while (fgets(line,255,tempfile) != NULL) {
    if (line[0] != '/') break;
    if(strstr(line,"Date:")){
      fclose(tempfile);
      if(strcmp(timestamp,line)==0) return FALSE;
      printf("Last written: %s",line);
      printf("Last read   : %s",timestamp);
      return TRUE;
    }
  }  
  printf("ERROR reading for the Date line in the %s.\n", fpcfile);
  return FALSE;
}

/**************************************************************
                  DEF: fpc_lockit
**************************************************************/
static BOOL fpc_lockit(char *filelock)
{
  struct passwd *uinfo;

  if (lockFd != -1)
     printf("Lock: Unbalanced call to fpc_lockit - continuing ...\n");

  lockFd = open (filelock, O_WRONLY | O_EXCL | O_CREAT | O_SYNC, 0644) ;
  if (lockFd == -1){ 
    printf("Cannot create %s.lock\n",fileName);
    return FALSE ;
  }
#if 0
     /* this causes problems for NSF, and is unnessary */
  if (lockf (lockFd, F_TLOCK, 0)) { /* non-blocking */
    printf("Cannot preform lockf on %s.lock\n",fileName);
    close (lockFd) ;
    lockFd = -1 ;
    return FALSE ;
  }
#endif
  uinfo = (struct passwd *) getpwuid((uid_t) geteuid());
  if (uinfo == NULL) {
     printf("**** Cannot get user id\n");
     //return -1;
	uinfo = (struct passwd*)malloc(sizeof(struct passwd));
	uinfo->pw_name = strdup("unknown");
  }

  write (lockFd, uinfo->pw_name, strlen(uinfo->pw_name)) ;

  return TRUE;
}
/************************************************************
                  DEF: remove_dead_file
*************************************************************/
static int remove_dead_file(char *filelock)
{
  struct stat stbuf;
  struct passwd *uinfo=NULL;
  char lockid[100], myid[100];
  int fd;

  if(stat(filelock, &stbuf) == -1){
       printf("Do not have permissions for %s\n",filelock);
       return FALSE;
  }
  uinfo = (struct passwd *) getpwuid((stbuf.st_uid));
  if(uinfo == NULL) {
       printf("Cannot find owner of lock file.\n");
       return FALSE;
  }
  strcpy(lockid,uinfo->pw_name);
  uinfo = (struct passwd *) getpwuid((uid_t) geteuid());
  strcpy(myid,uinfo->pw_name);

  printf("Project is locked by %s. Requesting userid is %s.\n",
            lockid, myid);

  if (strcmp(lockid, myid)!=0) return FALSE;
  if ((fd = open (filelock, O_WRONLY))== -1) return FALSE;
  if (lockf (fd, F_TLOCK, 0)) { /* non-blocking */
      printf("Someone using the same userid has an active lock on the file.\n");
      close(fd);
      return FALSE;
  }
  close(fd);
  if (Zbatch_flag) {
     printf("This is locked by your Userid. Removing lock.\n");
     unlink(filelock);
     return TRUE;
  }
  
  /* sness */
  /*if(graphQuery( */
  if(messQuery(
     "This is locked by your Userid.\nMust remove lock to set new lock. OK?"))
  {
      unlink(filelock);
      return TRUE;
  } 
  return FALSE;
}
/**************************************************************
                  DEF: caniwrite
**************************************************************/
static BOOL caniwrite()
{
  char filelock[MAXPATHLEN];

  if(strlen(dirName)>0)
       sprintf(filelock,"%s/%s.lock", dirName, fileName) ;
  else sprintf(filelock,"%s.lock", fileName) ;
      
  if(access(filelock, F_OK)==0)  /* already exists */
    if (!remove_dead_file(filelock)) return FALSE;

  if (fpc_lockit(filelock)) {
    if(hasdatechanged()){
      displaymess("The fpc file has been written since you read it.");
      fpc_unlockit();
      return FALSE;
    }
  }
  return TRUE;
}
/**************************************************************
                  DEF: fpc_unlockit
called when project is terminated 
        (Quit, before remove_old_project, box-out-of-range), 
or after save_fpc when writeaccess=FALSE (lock is not set on main menu)
or when lock is removed by user.
**************************************************************/
void fpc_unlockit()
{
  char filelock[MAXPATHLEN];

  if(strlen(dirName)>0)
       sprintf(filelock,"%s/%s.lock", dirName, fileName) ;
  else sprintf(filelock,"%s.lock", fileName) ;

     /* called in w/w2/graphsubs on crash, may not be locked */
  if(access(filelock, F_OK)) return; /* doesn't exist */

  if (lockFd != -1) {
    close (lockFd) ;
    lockFd = -1;
  }
#if 0
  if (lockFd != -1) {
    if (lockf (lockFd, F_ULOCK, 0)){ /* non-blocking */
      printf("Cannot unlock the lock file\n");
      return;
    }
    close (lockFd) ;
    lockFd = -1;
  }
  else {
     if(access(filelock, W_OK)) {
        printf("No write access for %s\n",filelock);
        return; /* locked by someelse */
     }
  }
#endif
  unlink(filelock);
  if (writeaccess) 
     printf("Remove lock for %s\n", fileName);
}
/******************************************************
                DEF: save_fpc_safe
*******************************************************/
int save_fpc_safe()
{
  int k;

  if(writeaccess){
    okaytowrite = TRUE;
    k = save_fpc();
  }
  else if(caniwrite()){
    okaytowrite = TRUE;
    k = save_fpc();
    if(!writeaccess)
      fpc_unlockit();
  }
  else { 
    okaytowrite = FALSE;
    k = save_fpc(); /* will write to project-user.fpc if requested */
  }
  return k;
} 
  
/******************************************************
                DEF: grantaccess
the writeaccess flag is used to set LOCK on main menu
*******************************************************/
void grantaccess()
{
  if(!dataloaded){
    displaymess("No fpc file has been created or read, so cannot lock.");
    return;
  }
  if(writeaccess){
      fpc_unlockit();
      writeaccess=FALSE;
      menu_main();
      return;
  }
 
  if(caniwrite()){
     writeaccess = TRUE;
     menu_main();
     printf("Write lock for %s\n", fileName);
     return;
  }
  else graphMessage("Write lock has been denied.");
}

